import type { enterItemsType, expressionItemsType } from "../handleBase/types";
import type { EditorType } from "@base/Editor/types";
import type { OperatorType } from "@base/Operator/types";
import { enterItemsEnum } from "../handleBase/enum";
import { TipsKindEnum, basicEditorEnum, quoteEditorEnum } from "../component/enum";
import { Editor } from "@base/Editor";
import { eventBus } from "./initEventBus";
import { isBracket, isEditor, isOperator } from "../handleBase/isInstanceOf";
import { BracketR } from "./initBaseElement";
import { computeExpression } from "../handleBase/computeResult";
import { BracketType } from "@base/Bracket/types";
import { translateExpressionToText } from "./translateCenter";
const store = eventBus.store; // 非实例值从store上获取，防止内存地址被修改
const { TypingRegister, BracketLIndexs, BracketRIndexs, Tips, Result, Formula } = eventBus.store

export const Clear = new Editor({
    basic: basicEditorEnum.clear,
    quote: quoteEditorEnum.clear
})
Clear.action = function () {
    // eventBus.store.reset();
    eventBus.emmit('resetStore');
}

export const Pop = new Editor({
    basic: basicEditorEnum.pop,
    quote: quoteEditorEnum.pop
})
Pop.action = function () {
    // 如果寄存器没有可退格的元素
    if (TypingRegister.isEmpty() && store.infixArr.length === 0) {
        // 前一次按了等于号，清空了中缀和寄存器，但保留了上一个的值
        (store.frontResult !== 0) && eventBus.emmit('setFrontResult', 0);
        return;
    }
    // 寄存器有退格的元素，删掉寄存器里的一个
    TypingRegister.tryPop();
    // 删除后，如果输入中的元素没有了，尝试重新从中缀表达式中取一个放回输入中
    if (TypingRegister.isEmpty() && store.infixArr.length !== 0) {
        let prevItem = store.infixArr.pop()!;
        if (isBracket(prevItem, 'left')) {
            BracketLIndexs.popPeekIndex();
        }
        if (isBracket(prevItem, 'right')) {
            BracketRIndexs.popPeekIndex();
        }
        TypingRegister.unPacket(prevItem)
    }
}

export const Equal = new Editor({
    basic: basicEditorEnum.equal,
    quote: quoteEditorEnum.equal
})
Equal.action = function () {
    const lastItemType = TypingRegister.getCurrentType();
    if (lastItemType === enterItemsEnum.initial) {
        Tips.show(TipsKindEnum.notice, '没有可计算的元素');
        return;
    }

    const lastItem = TypingRegister.getPacket()!;

    // 将正在输入的内容，手动推到中缀表达式中
    if ([enterItemsEnum.float, enterItemsEnum.int].includes(lastItemType)) {
        store.infixArr.push(lastItem);
    } else if (isBracket(lastItem, 'right')) {
        store.infixArr.push(lastItem);
        BracketRIndexs.pushNewIndex(store.infixArr.length - 1);
    }

    // 计算中缀表达式结果前，把中缀表达式末尾多余的运算符、左括号去掉，并补上足够的右括号
    let fixedInfixArr = correctInfixArr(store.infixArr).slice();
    eventBus.emmit('resetStore');
    Tips.hide();
    if (fixedInfixArr.length === 0) return;
    let newResult = computeExpression(fixedInfixArr);
    eventBus.emmit('setFrontResult', newResult);
    eventBus.emmit('setFrontInfixArr', fixedInfixArr);
}

/**
 * 把传入的中缀表达式数组末尾多余的运算符和左括号删除，并补全缺少的右括号。
 * 如果没有传递第二个参数会在原数组上删除和补全。
 * 有则浅拷贝数组，并把第二个参数加入浅拷贝的数组后，再删除和补全
 * @param targetInfixArr 中缀表达式数组
 * @param appen 要往中缀表达式末尾追加的元素
 * @returns 删除和补全后的中缀表达式数组
 */
export function correctInfixArr(targetInfixArr: expressionItemsType[], appen?: expressionItemsType): expressionItemsType[] {
    // 如果要计算的表达式追加一个元素，会拷贝原数组
    if (appen !== undefined && !isBracket(appen, 'left')) {
        targetInfixArr = targetInfixArr.slice();
        targetInfixArr.push(appen);
    }

    // 清掉中缀表达式中末尾,连着的所有 左括号、运算符
    // 特殊情况+（（（（（（
    while (targetInfixArr.length !== 0 && (isBracket(targetInfixArr.at(-1)!, 'left') || isOperator(targetInfixArr.at(-1)!))) {
        targetInfixArr.pop();
    }


    // 找到剔除多余左符号后实际左括号的数量，减去右括号的数量，得到欠缺右边括号的数量
    let needToAppenBracketR = BracketLIndexs.countBeforeItemOf(targetInfixArr.length - 1) - BracketRIndexs.countSize();
    for (let i = 0; i < needToAppenBracketR; i++) {
        targetInfixArr.push(BracketR);
    }

    return targetInfixArr;
}

// TODO 参数是一个空数组，异常处理，一次输入12个数，报错
// TODO 其实括号的action只在这里调用了，和运算符在中缀表达式中调用范畴有大区别
export function handleInput(enterItem: enterItemsType | EditorType) {
    if (typeof enterItem === 'string') {
        // 点击的是数字和小数点
        handleInputNum(enterItem);
    }
    else if (isBracket(enterItem) || isEditor(enterItem)) {
        (enterItem as EditorType | BracketType).action!();
    }
    else if (isOperator(enterItem)) {
        handleInputOperator(enterItem as OperatorType);
    }
    console.log(TypingRegister.getPacket());

    /* 页面显示更新 */

    // 点击等于号，
    if (typeof enterItem !== 'string' && enterItem.getBasic() === basicEditorEnum.equal) {
        // 结果dom放结果
        Result.updateText(store.frontResult);
        // 将等于号实例、前一次计算结果，放入前式子，将前式子组合为带等号的，空号间隔的文本式子
        Formula.updateText(translateExpressionToText([...store.frontInfixArr, Equal, store.frontResult], ' '));
        return;
    }
    if (TypingRegister.isEmpty()) {
        // 清空、退格、等于都会导致键入后，键入寄存器为空
        Tips.hide();
    }
    // 点击数字、退格（且没有清空寄存器）
    else if (typeof enterItem === 'string' || enterItem.getBasic() === basicEditorEnum.pop) {
        const previewInfixArr = correctInfixArr(store.infixArr, TypingRegister.getPacket());
        const previewResult = previewInfixArr.length ? computeExpression(previewInfixArr) : 0;
        Tips.show(TipsKindEnum.equal, previewResult);
    }

    // *中缀表达式数组转文本，插入式子dom
    // *寄存器(输入中的)内容全部转文本，插入结果dom
    Result.updateText(translateExpressionToText(TypingRegister.getItems()) || 0);
    Formula.updateText(translateExpressionToText(store.infixArr, ' '))

}

/**
 * 根据页面点击的数字、小数点，输入到输入寄存器中（输入前，把寄存器内容处理并放入中缀表达式）
 * @param numberOrDot 页面键盘中对应数字或小数点的字符串（先进入寄存器处理，不会进入任何表达式数组）
 * @returns void
 */
function handleInputNum(numberOrDot: string): void | never {
    const prevItem = TypingRegister.getPacket();
    const prevItemType = TypingRegister.getCurrentType();
    // 先判断寄存器里的内容能否进入中缀表达式
    if ([enterItemsEnum.initial, enterItemsEnum.float, enterItemsEnum.int].includes(prevItemType)) {
        // 无需读取TypingRegister里的元素到infixArr里
    }
    else if (isBracket(prevItem!, 'right')) {
        Tips.show(TipsKindEnum.notice, '右括号后面不能直接加数字');
        return;
    }
    else if (isBracket(prevItem!, 'left')) {
        store.infixArr.push(prevItem!);
        BracketLIndexs.pushNewIndex(store.infixArr.length - 1);
    }
    else if (isOperator(prevItem!)) {
        store.infixArr.push(prevItem!);
    }
    else {
        throw new Error("处理的数字型输入时，寄存器内容读取异常" + prevItem);
    }
    // 把数字放入寄存器
    TypingRegister.tryPush(numberOrDot);
}

/**
 * 根据页面点击的运算符，输入到输入寄存器中（输入前，把寄存器内容处理并放入中缀表达式）
 * @param operatorItem 页面键盘中对应操作符实例(先进入寄存器)
 * @returns void 
 */
function handleInputOperator(operatorItem: OperatorType): void | never {
    const prevItem = TypingRegister.getPacket();
    const prevItemType = TypingRegister.getCurrentType();
    if (prevItem === undefined) {
        // 寄存器为空，说明之前没输入东西
        store.infixArr.push(store.frontResult);
        eventBus.emmit('setFrontResult', 0);
        // Tips.show(TipsKindEnum.notice, '请先输入数字');
        // return;
    }
    // 处理寄存器上次存的内容
    else if ([enterItemsEnum.float, enterItemsEnum.int].includes(prevItemType)) {
        store.infixArr.push(prevItem);
    }
    else if (isBracket(prevItem, 'right')) {
        store.infixArr.push(prevItem);
        BracketRIndexs.pushNewIndex(store.infixArr.length - 1);
    }
    else if (isOperator(prevItem)) {
        // 丢弃上一个操作符输入
    }
    else if (isBracket(prevItem, 'left')) {
        /* store.infixArr.push(prevItem);
        BracketLIndexs.pushNewIndex(store.infixArr.length - 1);
        store.infixArr.push(0); // 自动补零，前一次是左括号，现在输入运算符，不合理 */
        Tips.show(TipsKindEnum.notice, '请先输入数字');
        return;
    }
    else {
        console.log(prevItem);

        throw new Error("处理的运算符型输入时，寄存器内容读取异常" + prevItem);
    }
    // 此次输入替换寄存器中的内容
    TypingRegister.tryPush(operatorItem);
}
